package _0000000000service.sm;

import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.math.ec.ECPoint;

import java.io.UnsupportedEncodingException;
import java.math.BigInteger;

public class SM2Utils {

    public static final String PRIK="29A5479D07317F5E76867537AC2E0681DC9A09BAD222E66F9E5027B1FC8D86FB";


    /**
     * 加密模式
     */
    public enum SM2CipherMode {
        C1C2C3(0), C1C3C2(1);
        private int code;

        SM2CipherMode(int code) {
            this.code = code;
        }

        public int getCode() {
            return this.code;
        }
    }

    //生成随机秘钥对
    public static void generateKeyPair() {
        SM2 sm2 = SM2.Instance();
        AsymmetricCipherKeyPair key = sm2.ecc_key_pair_generator.generateKeyPair();
        ECPrivateKeyParameters ecpriv = (ECPrivateKeyParameters) key.getPrivate();
        ECPublicKeyParameters ecpub = (ECPublicKeyParameters) key.getPublic();
        BigInteger privateKey = ecpriv.getD();
        ECPoint publicKey = ecpub.getQ();

        System.out.println("公钥: " + Util.byteToHex(publicKey.getEncoded()));
        System.out.println("私钥: " + Util.byteToHex(privateKey.toByteArray()));
    }

    //数据加密
    public static String encrypt(String publicKey, String data) {
        return SM2Utils.encrypt(Util.hexToByte(publicKey), data.getBytes(), SM2CipherMode.C1C3C2.code);
    }

    public static String encrypt(byte[] publicKey, byte[] data, int cipherMode) {
        if (publicKey == null || publicKey.length == 0) {
            return null;
        }

        if (data == null || data.length == 0) {
            return null;
        }

        byte[] source = new byte[data.length];
        System.arraycopy(data, 0, source, 0, data.length);

        Cipher cipher = new Cipher();
        SM2 sm2 = SM2.Instance();
        ECPoint userKey = sm2.ecc_curve.decodePoint(publicKey);

        ECPoint c1 = cipher.Init_enc(sm2, userKey);
        cipher.Encrypt(source);
        byte[] c3 = new byte[32];
        cipher.Dofinal(c3);

//		System.out.println("C1 " + Util.byteToHex(c1.getEncoded()));
//		System.out.println("C2 " + Util.byteToHex(source));
//		System.out.println("C3 " + Util.byteToHex(c3));
        //C1 C2 C3拼装成加密字串
        if (cipherMode == SM2CipherMode.C1C2C3.code) {
            return Util.byteToHex(c1.getEncoded()) + Util.byteToHex(source) + Util.byteToHex(c3);//C1C2C3
        } else {
            return Util.byteToHex(c1.getEncoded()) + Util.byteToHex(c3) + Util.byteToHex(source);//C1C3C2
        }

    }

    //数据解密
    public static String decrypt(String privateKey, String encryptedData) {
        return new String(SM2Utils.decrypt(Util.hexToByte(privateKey), Util.hexToByte(encryptedData), SM2CipherMode.C1C3C2.code));
    }
    //数据解密
    public static String decrypt(String privateKey, String encryptedData, int cipherMode) throws UnsupportedEncodingException {
        return new String(SM2Utils.decrypt(Util.hexToByte(privateKey), Util.hexToByte(encryptedData), cipherMode), "utf8");
    }
    //数据解密
    public static String decrypt(String encryptedData) {
        return new String(SM2Utils.decrypt(Util.hexToByte(PRIK), Util.hexToByte(encryptedData), SM2CipherMode.C1C3C2.code));
    }

    public static byte[] decrypt(byte[] privateKey, byte[] encryptedData, int cipherMode) {
        if (privateKey == null || privateKey.length == 0) {
            return null;
        }

        if (encryptedData == null || encryptedData.length == 0) {
            return null;
        }
        //加密字节数组转换为十六进制的字符串 长度变为encryptedData.length * 2
        String data = Util.byteToHex(encryptedData);

        /***分解加密字串
         * （C1 = C1标志位2位 + C1实体部分128位 = 130）
         * （C3 = C3实体部分64位  = 64）
         * （C2 = encryptedData.length * 2 - C1长度  - C2长度）
         */
        byte[] c1Bytes, c2, c3;
        if (cipherMode == SM2CipherMode.C1C2C3.code) {
            //C1C2C3
            c1Bytes = Util.hexToByte(data.substring(0, 130));
            c2 = Util.hexToByte(data.substring(130, data.length() - 64));
            c3 = Util.hexToByte(data.substring(data.length() - 64));
        } else {
            //C1C3C2
            c1Bytes = Util.hexToByte(data.substring(0, 130));
            c3 = Util.hexToByte(data.substring(130, 130 + 64));
            c2 = Util.hexToByte(data.substring(130 + 64));
        }

        SM2 sm2 = SM2.Instance();
        BigInteger userD = new BigInteger(1, privateKey);

        //通过C1实体字节来生成ECPoint
        ECPoint c1 = sm2.ecc_curve.decodePoint(c1Bytes);
        Cipher cipher = new Cipher();
        cipher.Init_dec(userD, c1);
        cipher.Decrypt(c2);
        cipher.Dofinal(c3);

        //返回解密结果
        return c2;
    }

    public static void main(String[] args) throws Exception {
        //生成密钥对
        generateKeyPair();

        String plainText = "{\"冷\"}";

        //下面的秘钥可以使用generateKeyPair()生成的秘钥内容
        // 国密规范正式私钥
        String prik = "6DE31E9C1C9960FF767C82E427DA61EC759DD50997EA7C8B87CB8FAA4BF1DF0B";
        // 国密规范正式公钥
        String pubk = "04634330FDF82C5DE5B3544D7CA57CDEA74AC9892A25FCCC2F23644D03409283B207B5C5F00471F7C9547A1EC896DECC6234EEFA5BE44921DBA4C0EA95DEDC73DA";
//      String prik = "3690655E33D5EA3D9A4AE1A1ADD766FDEA045CDEAA43A9206FB8C430CEFE0D94";
        // 国密规范正式公钥
//      String pubk = "04F6E0C3345AE42B51E06BF50B98834988D54EBC7460FE135A48171BC0629EAE205EEDE253A530608178A98F1E19BB737302813BA39ED3FA3C51639D7A20C7391A";

        System.out.println("加密: ");
        String cipherText = SM2Utils.encrypt(Util.hexToByte(pubk), plainText.getBytes(), SM2CipherMode.C1C3C2.code);
        //String cipherText = SM2Utils.encrypt(pubk, plainText);
        System.out.println(cipherText);
        System.out.println("解密: ");
        //plainText = SM2Utils.decrypt(prik, "04f9f459bb079c30c60e0d8349e59346a21662d764a433d1d7404275d66e6040443d4ed9520f96738d0936be3de9402573ef95f58028182ca97ceb991b6c77bab960f8bdec92ae9b74062b1cd371b78bf8b1e80df5bbccce5d5d1071235e6016a9fb8dfbe88eab1933e4d06363b76b755d3dcce6ca54f7aeb8459837ec76bc52d0a767309875d0c1aca47b988a76c8f639a4d2288348a018553a2aa831aeeb10b676bf382513a5bdacfc95e97006924dc8c103cbc281aed2b49d2af3474b425f0662b0300af2de7b2e9f1a04ba1941d6859d556d21360dfc6747478bb99ad7f10e1f69268f845bef6fca4c2c5dd3597ea0fcd8af159bce930f05d08fbf6a5dea5f269b073ef0f3964185923435ef200e29e9abc0066a6203ff57c4dcc5b8dd898357cfa44b1c6b0326a8cd566f8b9d4fc3a10c8af66cf0ed4aa4a6cc75cfa5b6470d016ffaef97555e42f3248e08033c63a89425ff992a20de798ededf255a18ef83b175c9fc7c79d5b6e4ae5c718ea8a1b5499fb6747c8ea4ea", SM2CipherMode.C1C3C2.code);
        plainText = SM2Utils.decrypt(prik, "cipherText", SM2CipherMode.C1C3C2.code);
        System.out.println(plainText);

    }
}
